#include "Perlin.h"

#include <stdlib.h>
#include <math.h>
#include <time.h>

#define max(a,b) (a>=b?a:b)
#define min(a,b) (a<=b?a:b)

const double pi = 3.14159265;

static int taille = 0;
static int pas2D = 0;
static int nombre_octaves2D = 0;
static int hauteur = 0;
static int longueur = 0;
static int longueur_max = 0;
static int hauteur_max = 0;
static double* valeurs2D;

/*static double interpolation_cubique(double y0, double y1, double y2, double y3, double x)
{
     double a = y3 - y2 - y0 + y1;
     double b = y0 - y1 - a;
     double c = y2 - y0;
     double d = y1;
     return a *x * x * x + b * x * x + c * x + d;
}*/

static double interpolation_cos1D(double a, double b, double x)
{
	double k = (1 - cos(x * pi)) / 2;
    return a * (1 - k) + b * k;
}

void initPerlin2D(int l, int h, int p, int n)
{
    nombre_octaves2D = n;
    if(taille != 0)
        free(valeurs2D);
    longueur = l;
    hauteur = h;
    pas2D = p;
    longueur_max = (int) ceil(longueur * pow(2, nombre_octaves2D  - 1)  / pas2D);
    hauteur_max = (int) ceil(hauteur * pow(2, nombre_octaves2D  - 1)  / pas2D);

    valeurs2D = (double*) malloc(sizeof(double) * longueur_max * hauteur_max);

    srand(time(NULL));
    int i;
    for(i = 0; i < longueur_max * hauteur_max; i++)
        valeurs2D[i] = ((double) rand()) / RAND_MAX;
}

void destroyPerlin2D()
{
    if(longueur != 0)
        free(valeurs2D);
    longueur = 0;
}

static double bruit2D(int i, int j) {
    return valeurs2D[i * longueur_max + j];
}

/*static double interpolation_cubique2D(double y00, double y01, double y02, double y03, 
	double y10, double y11, double y12, double y13,
	double y20, double y21, double y22, double y23,
	double y30, double y31, double y32, double y33,
	double x, double y)
{
   double v0 = interpolation_cubique(y00,y01,y02,y03,x);
   double v1 = interpolation_cubique(y10,y11,y12,y13,x);
   double v2 = interpolation_cubique(y20,y21,y22,y23,x);
   double v3 = interpolation_cubique(y30,y31,y32,y33,x);
   return interpolation_cubique(v0,v1,v2,v3,y);
}*/

static double interpolation_cos2D(double a, double b, double c, double d, double x, double y)
{
   double y1 = interpolation_cos1D(a, b, x);
   double y2 = interpolation_cos1D(c, d, x);
   return  interpolation_cos1D(y1, y2, y);
}

static double fonction_bruit2D(double x, double y)
{
   int i = (int) (x / pas2D);
   int j = (int) (y / pas2D);
   return interpolation_cos2D(bruit2D(i, j), bruit2D(i + 1, j), bruit2D(i, j + 1), bruit2D(i + 1, j + 1), fmod(x / pas2D, 1), fmod(y / pas2D, 1));
   /*return interpolation_cubique2D(
		bruit2D(max(0, i - 1), max(0, j - 1)),            bruit2D(min(0, i - 1), j),            bruit2D(max(0, i - 1), j + 1),            bruit2D(max(0, i - 1), min(j + 2, hauteur_max)),
		bruit2D(min(i, longueur_max), max(0, j - 1)),     bruit2D(min(i, longueur_max), j),     bruit2D(min(i, longueur_max), j + 1),     bruit2D(min(i, longueur_max), min(j + 2, hauteur_max)),
		bruit2D(min(i + 1, longueur_max), max(0, j - 1)), bruit2D(min(i + 1, longueur_max), j), bruit2D(min(i + 1, longueur_max), j + 1), bruit2D(min(i + 1, longueur_max), min(j + 2, hauteur_max)),
		bruit2D(min(i + 2, longueur_max), max(0, j - 1)), bruit2D(min(i + 2, longueur_max), j), bruit2D(min(i + 2, longueur_max), j + 1), bruit2D(min(i + 2, longueur_max), min(j + 2, hauteur_max)),
		fmod(x / pas2D, 1),
		fmod(y / pas2D, 1));*/
}

double bruit_de_perlin2D(double x, double y, double persistance)
{
    double perlin = 0;
    double p = 1;
    int f = 1;
    int i;

    for(i = 0 ; i < nombre_octaves2D ; i++) {
        perlin += p * fonction_bruit2D(x * f, y * f);
        p *= persistance;
        f *= 2;
    }
    return perlin * (1 - persistance) / (1 - p);
}
